#include <string>
#include <cstdio>
#include <exception>

#include "dag.h"
#include "gameState.h"

using namespace std;

static GameState state;
static Colour our_colour;
static bool force_mode = false;
static Dag<GameState>* dag;

class IllegalMove: public exception {
};
class MoveParseError: public exception {
};

static string get_move_description(Move const& m)
{
  string res;
  
  res += 'a' + m.source_col;
  res += '8' - m.source_row;

  res += 'a' + m.destination_col;
  res += '8' - m.destination_row;

  if (m.type == PROMOTION_FOR_ROOK) {
    res += 'R';
  } else if (m.type == PROMOTION_FOR_KNIGHT) {
    res += 'N';
  } else if (m.type == PROMOTION_FOR_BISHOP) {
    res += 'B';
  } else if (m.type == PROMOTION_FOR_QUEEN) {
    res += 'Q';
  }

  return res;
}

static Move parse_move(string const& descr)
{
  if (descr.size() < 4 || descr.size() > 5) throw MoveParseError();
  if (descr[0] < 'a' || descr[0] > 'h') throw MoveParseError();
  if (descr[1] < '1' || descr[1] > '8') throw MoveParseError();
  if (descr[2] < 'a' || descr[2] > 'h') throw MoveParseError();
  if (descr[3] < '1' || descr[3] > '8') throw MoveParseError();

  int sc = descr[0] - 'a';
  int sr = '8' - descr[1];
  int dc = descr[2] - 'a';
  int dr = '8' - descr[3];

  if (descr.size() == 5) {
    // promotion
    if (descr[4] == 'q' || descr[4] == 'Q') {
      Move m(sr, sc, dr, dc, PROMOTION_FOR_QUEEN);
      if (!state.movePossible(m)) throw IllegalMove();
      return m;
    } else if (descr[4] == 'r' || descr[4] == 'R') {
      Move m(sr, sc, dr, dc, PROMOTION_FOR_ROOK);
      if (!state.movePossible(m)) throw IllegalMove();
      return m;
    } else if (descr[4] == 'b' || descr[4] == 'B') {
      Move m(sr, sc, dr, dc, PROMOTION_FOR_BISHOP);
      if (!state.movePossible(m)) throw IllegalMove();
      return m;
    } else if (descr[4] == 'n' || descr[4] == 'N') {
      Move m(sr, sc, dr, dc, PROMOTION_FOR_KNIGHT);
      if (!state.movePossible(m)) throw IllegalMove();
      return m;
    } else throw MoveParseError();
  } else {
    vector<Move> moves = state.generateAllMoves();
    for (vector<Move>::iterator it = moves.begin(); it != moves.end(); it++) {
      if (it->source_col == sc && it->source_row == sr && it->destination_row == dr && it->destination_col == dc) {
	return *it;
      }
    }
    throw IllegalMove();
  }
}

static void expand(GameState const& state, int depth)
{
  if (depth == 0) return;

  dag->expand(state);

  std::list<GameState> children = dag->get_children(state);
  std::list<GameState>::iterator it;

  for (it = children.begin(); it != children.end(); it++ ) {
    expand(*it, depth - 1);
  }
  dag->update_value(state);
}

static void make_move()
{
  expand(state, 3);

  if (!dag->has_children(state)) return;

  Move m = dag->best_move(state);
  string descr = get_move_description(m);

  printf("move %s\n", descr.c_str());

  state = dag->commit(m);
}

static void check_result()
{
  std::vector<Move> moves = state.generateAllMoves();
  if (moves.size() != 0) return;
  if (state.isPlayerUnderCheck) {
    if (state.round == BLACK) {
      puts("1-0 (White wins)");
    } else {
      puts("0-1 (Black wins)");
    }
  } else {
    puts("1/2-1/2 (Draw game)");
  }
}


static bool process_command(string const& cmd, string const& arg)
{
  if (cmd == "protover") {
    puts("feature analyze=0");
    puts("feature colors=0");
    puts("feature draw=0");
    puts("feature name=0");
    puts("feature pause=0");
    puts("feature ping=1");
    puts("feature playother=1");
    puts("feature sigint=0");
    puts("feature sigterm=0");
    puts("feature time=0");
    puts("feature usermove=1");
    puts("feature variants=\"normal\"");

    puts("feature done=1");
  } else if (cmd == "new") {
    state = GameState();
    our_colour = BLACK;
    force_mode = false;
    delete dag;
    dag = new Dag<GameState>(state);
  } else if (cmd == "force") {
    force_mode = true;
  } else if (cmd == "go") {
    force_mode = false;
    our_colour = state.round;
    make_move();
  } else if (cmd == "playother") {
    force_mode = false;
    our_colour = Util::opposite(state.round);
  } else if (cmd == "usermove") {
    try {
      Move m = parse_move(arg);
      state = dag->commit(m);
      check_result(); // will send result if would be known
      if (!force_mode && state.round == our_colour) {
	make_move();
	check_result();
      }
    } catch (IllegalMove&) {
      printf("Illegal move: %s\n", arg.c_str());
    } catch (MoveParseError&) {
      printf("Error (unrecognized move): %s %s\n", cmd.c_str(), arg.c_str());
    }
  } else if (cmd == "ping") {
    printf("pong %s\n", arg.c_str()); //setbord, edit for later - one of them
  } else if (cmd == "quit") {
    return true;
  } else if (cmd != "xboard" && cmd != "accepted" && cmd != "variant" &&
      cmd != "random" && cmd != "result") {
    printf("Error (unknown command): %s %s\n", cmd.c_str(), arg.c_str());
  }

  return false;
}

int main()
{
  setbuf(stdout, NULL);
  puts("feature done=0");

  while (true) {
    char buf[1024];

    if (fgets(buf, sizeof buf, stdin) == NULL) break;

    string line(buf);
    size_t n = line.size();
    if (n > 0 && line[n-1] == '\n') line.resize(n-1);
    string cmd, arg;

    size_t pos = line.find(" ");
    if (pos == string::npos) {
      cmd = line;
    } else {
      cmd = line.substr(0, pos);
      arg = line.substr(pos + 1);
    }

    bool done = process_command(cmd, arg);
    if (done) break;
  }

  return 0;
}
